package chat_server

import (
	"errors"
	"fmt"
	"net"
	"sync"
	"sync/atomic"
)

type tChatServer struct {
	openFlag int32
	closeFlag int32

	clients []IChatClient
	clientCount int
	clientLock *sync.RWMutex

	listener net.Listener
	recvLogs []IMsg
}

func NewChatServer() IChatServer {
	it := &tChatServer{
		openFlag: 0,
		closeFlag: 0,

		clients: []IChatClient{},
		clientCount: 0,
		clientLock: new(sync.RWMutex),

		listener: nil,
		recvLogs: []IMsg{},
	}
	return it
}

func (me *tChatServer) Open(port int) error {
	if !atomic.CompareAndSwapInt32(&me.openFlag, 0, 1) {
		return errors.New("server already opened")
	}

	listener, err := net.Listen("tcp", fmt.Sprintf(":%v", port))
	if err != nil {
		return err
	}

	me.listener = listener
	go me.beginListening()
	return nil
}


func (me *tChatServer) isClosed() bool {
	return me.closeFlag != 0
}

func (me *tChatServer) isNotClosed() bool {
	return !me.isClosed()
}

func (me *tChatServer) beginListening() {
	for !me.isClosed() {
		conn, err := me.listener.Accept()
		if err != nil {
			me.Close()
			break
		}

		me.handleIncomingConn(conn)
	}
}


func (me *tChatServer) Close() {
	if !atomic.CompareAndSwapInt32(&me.closeFlag, 0, 1) {
		return
	}

	_ = me.listener.Close()
	me.closeAllClients()
}

func (me *tChatServer) closeAllClients() {
	me.clientLock.Lock()
	defer me.clientLock.Unlock()

	for i,it := range me.clients {
		if it != nil {
			it.Close()
			me.clients[i] = nil
		}
	}
	me.clientCount = 0
}


func (me *tChatServer) handleIncomingConn(conn net.Conn) {
	// init client
	client := openChatClient(conn, true)
	client.RecvHandler(me.handleClientMsg)
	client.CloseHandler(me.handleClientClosed)

	// lock me.clients
	me.clientLock.Lock()
	defer me.clientLock.Unlock()

	// append to me.clients
	if len(me.clients) > me.clientCount {
		me.clients[me.clientCount] = client
	} else {
		me.clients = append(me.clients, client)
	}
	me.clientCount++

	Logging.Logf("tChatServer.handleIncomingConn, clientCount=%v", me.clientCount)
}

func (me *tChatServer) handleClientMsg(client IChatClient, msg IMsg) {
	me.recvLogs = append(me.recvLogs, msg)

	if nameMsg,ok := msg.(*NameMsg);ok {
		client.SetName(nameMsg.Name)

	} else if _, ok := msg.(*ChatMsg);ok {
		me.Broadcast(msg)
	}
}

func (me *tChatServer) handleClientClosed(client IChatClient) {
	Logging.Logf("tChatServer.handleClientClosed, %s", client.GetName())

	me.clientLock.Lock()
	defer me.clientLock.Unlock()

	if me.clientCount <= 0 {
		return
	}

	lastI := me.clientCount - 1
	for i,it := range me.clients {
		if it == client {
			if i == lastI {
				me.clients[i] = nil
			} else {
				me.clients[i], me.clients[lastI] = me.clients[lastI], nil
			}
			me.clientCount--
			break
		}
	}

	Logging.Logf("tChatServer.handleClientClosed, %s, clientCount=%v", client.GetName(), me.clientCount)
}

func (me *tChatServer) Broadcast(msg IMsg) {
	me.clientLock.RLock()
	defer me.clientLock.RUnlock()

	for _,it := range me.clients {
		if it != nil {
			it.Send(msg)
		}
	}
}